﻿using ExDUIR.NET.Control;
using ExDUIR.NET.Declaration;
using ExDUIR.NET.Handle;
using ExDUIR.NET.Utility;
using static ExDUIR.NET.Const.ExConst;
using System.Text;
using ExDUIR.NET.Color;
using ExDUIR.NET.Common;

namespace ExDUIR.NET.Skin
{
    public sealed class ExSkinBuilder
    {
        #region 配置参数类
        private class SkinCreationOptions
        {
            public ExSkin Parent { get; set; }
            public IntPtr HwndParent { get; set; }
            public string ClassName { get; set; }
            public string WindowName { get; set; } = "ExDUIR Window";
            public int X { get; set; } = 0;
            public int Y { get; set; } = 0;
            public int Width { get; set; } = 800;
            public int Height { get; set; } = 600;
            public int DuiStyle { get; set; } = WINDOW_STYLE_MAINWINDOW | WINDOW_STYLE_BUTTON_CLOSE | WINDOW_STYLE_BUTTON_MIN | WINDOW_STYLE_MOVEABLE | WINDOW_STYLE_CENTERWINDOW |
                WINDOW_STYLE_TITLE | WINDOW_STYLE_SIZEABLE | WINDOW_STYLE_HASICON;
            public int Win32Style { get; set; } = 0; // WS_VISIBLE | WS_CLIPCHILDREN
            public int Win32StyleEx { get; set; } = 0;
            public IntPtr ThemeHandle { get; set; } = IntPtr.Zero;
            public ExWndProcDelegate WndProc { get; set; }
            public bool BindToExisting { get; set; }
            public IntPtr ExistingHwnd { get; set; }
        }
        #endregion

        private readonly SkinCreationOptions _options = new();

        /// <summary>
        /// 绑定到现有窗口的构建器入口
        /// </summary>
        public static ExSkinBuilder BindExisting(IntPtr existingHwnd)
        {
            if (existingHwnd == IntPtr.Zero)
                throw new ArgumentException("Invalid window handle", nameof(existingHwnd));

            return new ExSkinBuilder
            {
                _options =
            {
                BindToExisting = true,
                ExistingHwnd = existingHwnd
            }
            };
        }

        public ExSkinBuilder WithParent(ExSkin parent)
        {
            _options.Parent = parent ?? throw new ArgumentNullException(nameof(parent));
            return this;
        }

        public ExSkinBuilder WithPosition(int x, int y)
        {
            _options.X = x;
            _options.Y = y;
            return this;
        }

        public ExSkinBuilder WithSize(int width, int height)
        {
            if (width <= 0 || height <= 0)
                throw new ArgumentException("Size must be positive");

            _options.Width = width;
            _options.Height = height;
            return this;
        }

        public ExSkinBuilder WithWindowName(string name)
        {
            _options.WindowName = name ?? throw new ArgumentNullException(nameof(name));
            return this;
        }

        public ExSkinBuilder WithStyles(
            int duiStyle = 0,
            int win32Style = 0,
            int win32StyleEx = 0)
        {
            _options.DuiStyle = duiStyle;
            _options.Win32Style = win32Style;
            _options.Win32StyleEx = win32StyleEx;
            return this;
        }

        public ExSkinBuilder WithTheme(IntPtr themeHandle)
        {
            _options.ThemeHandle = themeHandle;
            return this;
        }

        public ExSkinBuilder WithWndProc(ExWndProcDelegate wndProc)
        {
            _options.WndProc = wndProc ?? throw new ArgumentNullException(nameof(wndProc));
            return this;
        }

        public ExSkin Build()
        {
            if (_options.BindToExisting)
            {
                return BindToExistingWindow();
            }
            return CreateNewWindow();
        }

        private ExSkin CreateNewWindow()
        {
            var parentHwnd = _options.Parent?.Hwnd ?? IntPtr.Zero;

            var hWnd = ExAPI.Ex_WndCreate(
                parentHwnd,
                _options.ClassName,
                _options.WindowName,
                _options.X,
                _options.Y,
                _options.Width,
                _options.Height,
                _options.Win32Style,
                _options.Win32StyleEx);

            if (hWnd == IntPtr.Zero)
                throw new InvalidOperationException("Window creation failed");

            return new ExSkin(hWnd, _options.DuiStyle, _options.ThemeHandle, _options.WndProc);
        }

        private ExSkin BindToExistingWindow()
        {
            return new ExSkin(
                _options.ExistingHwnd,
                _options.DuiStyle,
                _options.ThemeHandle,
                _options.WndProc);
        }
    }

    public class ExSkin : IBase
    {// 提供静态方法创建Builder
        public static ExSkinBuilder CreateBuilder() => new ExSkinBuilder();
        private readonly ExSkinHandle _handle;
        public IntPtr Hwnd;
        public int Handle => _handle.Handle;
        public bool IsValid => _handle.IsInvalid;

        internal ExSkin(ExSkinHandle handle)
        {
            _handle = handle;
        }

        internal ExSkin(IntPtr hWnd)
        {
            _handle = new ExSkinHandle(ExAPI.Ex_DUIFromWindow(hWnd));
        }

        /// <summary>
        /// 创建界面
        /// </summary>
        /// <param name="pOwner">父界面</param>
        /// <param name="sClassName">窗口类名,可以是null</param>
        /// <param name="sWindowName">窗口标题</param>
        /// <param name="x">横坐标</param>
        /// <param name="y">纵坐标</param>
        /// <param name="nWidth">宽度</param>
        /// <param name="nHeight">高度</param>
        /// <param name="dwStyleDUI">界面风格WINDOW_STYLE_</param>
        /// <param name="dwStyle">win32窗口风格</param>
        /// <param name="dwStyleEx">win32窗口扩展风格</param>
        /// <param name="hTheme">主题包句柄,可以空</param>
        /// <param name="pfnWndProc">窗口回调</param>
        internal ExSkin(ExSkin pOwner, string sClassName, string sWindowName, int x, int y, int nWidth, int nHeight, int dwStyleDUI,
            int dwStyle = 0, int dwStyleEx = 0, IntPtr hTheme = default, ExWndProcDelegate pfnWndProc = null)
        {
            IntPtr hWndParent = pOwner == null ? IntPtr.Zero : pOwner.Hwnd;
            Hwnd = ExAPI.Ex_WndCreate(hWndParent, sClassName, sWindowName, x, y, nWidth, nHeight, dwStyle, dwStyleEx);
            if (Hwnd != IntPtr.Zero)
            {
                _handle = new ExSkinHandle(ExAPI.Ex_DUIBindWindowEx(Hwnd, hTheme, dwStyleDUI, IntPtr.Zero, pfnWndProc));
            }
        }

        /// <summary>
        /// 创建界面
        /// </summary>
        /// <param name="pWindow">父窗口</param>
        /// <param name="dwStyleDUI">界面风格WINDOW_STYLE_</param>
        /// <param name="hTheme">主题包句柄,可以空</param>
        /// <param name="pfnWndProc">窗口回调</param>
        internal ExSkin(IntPtr HwndHandle, int dwStyleDUI, IntPtr hTheme = default, ExWndProcDelegate pfnWndProc = null)
        {
            _handle = new ExSkinHandle(ExAPI.Ex_DUIBindWindowEx(HwndHandle, hTheme, dwStyleDUI, IntPtr.Zero, pfnWndProc));
        }

        /// <summary>
        /// 启用/禁用窗口
        /// </summary>
        public bool Enable { get => WinAPI.IsWindowEnabled(Hwnd); set => WinAPI.EnableWindow(Hwnd, value); }

        /// <summary>
        /// 取/置 窗口可视
        /// </summary>
        public bool Visible { get => WinAPI.IsWindowVisible(Hwnd); set => ExAPI.Ex_DUIShowWindow(_handle.Handle, value ? 1 : 0, 0, 0, 0); }

        /// <summary>
        /// 取客户区矩形
        /// </summary>
        public ExRect ClientRect
        {
            get
            {
                ExAPI.Ex_DUIGetClientRect(_handle.Handle, out var rc);
                return rc;
            }
        }

        /// <summary>
        /// 取窗口矩形
        /// </summary>
        public ExRect WindowRect
        {
            get
            {
                WinAPI.GetWindowRect(Hwnd, out var rc);
                return rc;
            }
        }

        /// <summary>
        /// 是否是窗口
        /// </summary>
        public bool Validate => WinAPI.IsWindow(Hwnd) && IsValid;

        /// <summary>
        /// 窗口透明度
        /// </summary>
        public int Alpha
        {
            get
            {
                return (int)ExAPI.Ex_DUIGetLong(_handle.Handle, ENGINE_LONG_ALPHA);
            }
            set => ExAPI.Ex_DUISetLong(_handle.Handle, ENGINE_LONG_ALPHA, (IntPtr)value);
        }

        /// <summary>
        /// 窗口模糊度
        /// </summary>
        public int Blur
        {
            get
            {
                return (int)ExAPI.Ex_DUIGetLong(_handle.Handle, ENGINE_LONG_BLUR);
            }
            set
            {
                ExAPI.Ex_DUISetLong(_handle.Handle, ENGINE_LONG_BLUR, (IntPtr)value);
            }
        }

        /// <summary>
        /// 窗口背景色
        /// </summary>
        public ExARGB BackgroundColor
        {
            set => ExAPI.Ex_DUISetLong(_handle.Handle, ENGINE_LONG_CRBKG, (IntPtr)value);
            get => (int)ExAPI.Ex_DUIGetLong(_handle.Handle, ENGINE_LONG_CRBKG);
        }

        /// <summary>
        /// 窗口边框色
        /// </summary>
        public ExARGB BorderColor
        {
            get
            {
                return (int)ExAPI.Ex_DUIGetLong(_handle.Handle, ENGINE_LONG_CRBORDER);
            }
            set
            {
                ExAPI.Ex_DUISetLong(_handle.Handle, ENGINE_LONG_CRBORDER, (IntPtr)value);
            }
        }

        /// <summary>
        /// 窗口阴影色
        /// </summary>
        public ExARGB ShadowColor
        {
            get
            {
                return (int)ExAPI.Ex_DUIGetLong(_handle.Handle, ENGINE_LONG_CRSD);
            }
            set
            {
                ExAPI.Ex_DUISetLong(_handle.Handle, ENGINE_LONG_CRSD, (IntPtr)value);
            }
        }

        /// <summary>
        /// 窗口最小高度
        /// </summary>
        public int MinHeight
        {
            get
            {
                return (int)ExAPI.Ex_DUIGetLong(_handle.Handle, ENGINE_LONG_MINHEIGHT);
            }
            set
            {
                ExAPI.Ex_DUISetLong(_handle.Handle, ENGINE_LONG_MINHEIGHT, (IntPtr)value);
            }
        }

        /// <summary>
        /// 窗口最小宽度
        /// </summary>
        public int MinWidth
        {
            get
            {
                return (int)ExAPI.Ex_DUIGetLong(_handle.Handle, ENGINE_LONG_MINWIDTH);
            }
            set
            {
                ExAPI.Ex_DUISetLong(_handle.Handle, ENGINE_LONG_MINWIDTH, (IntPtr)value);
            }
        }

        /// <summary>
        /// 窗口圆角度
        /// </summary>
        public int Radius
        {
            get
            {
                return (int)ExAPI.Ex_DUIGetLong(_handle.Handle, ENGINE_LONG_RADIUS);
            }
            set
            {
                ExAPI.Ex_DUISetLong(_handle.Handle, ENGINE_LONG_RADIUS, (IntPtr)value);
            }
        }

        /// <summary>
        /// 窗口自定义参数
        /// </summary>
        public IntPtr LParam
        {
            get
            {
                return ExAPI.Ex_DUIGetLong(_handle.Handle, ENGINE_LONG_LPARAM);
            }
            set
            {
                ExAPI.Ex_DUISetLong(_handle.Handle, ENGINE_LONG_LPARAM, value);
            }
        }

        /// <summary>
        /// 取/置窗口标题
        /// </summary>
        public string Text
        {
            get
            {
                int cch = WinAPI.GetWindowTextLength(Hwnd);
                StringBuilder sb = new StringBuilder(cch);
                WinAPI.GetWindowText(Hwnd, sb, cch + 1);
                return sb.ToString();
            }
            set
            {
                WinAPI.SetWindowText(Hwnd, value);
            }
        }

        /// <summary>
        /// 标题栏组件
        /// </summary>
        public ExControl Caption
        {
            get
            {
                return new ExControl(new ExControlHandle((int)ExAPI.Ex_DUIGetLong(_handle.Handle, ENGINE_LONG_OBJCAPTION)));
            }
        }

        /// <summary>
        /// 取背景图片信息
        /// </summary>
        /// <param name="pImageInfo"></param>
        /// <returns></returns>
        public bool GetBackgroundImage(out ExBackgroundImageInfo pImageInfo)
        {
            return ExAPI.Ex_ObjGetBackgroundImage(_handle.Handle, out pImageInfo);
        }

        /// <summary>
        /// 取引擎数值
        /// </summary>
        /// <param name="nIndex"></param>
        /// <returns></returns>
        public IntPtr GetLong(int nIndex)
        {
            return ExAPI.Ex_DUIGetLong(_handle.Handle, nIndex);
        }

        /// <summary>
        /// 移动窗口
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <param name="nWidth"></param>
        /// <param name="nHeight"></param>
        /// <param name="fRedraw"></param>
        /// <returns></returns>
        public bool Move(int x, int y, int nWidth, int nHeight, bool fRedraw = true)
        {
            return WinAPI.MoveWindow(Hwnd, x, y, nWidth, nHeight, fRedraw);
        }

        /// <summary>
        /// 非阻塞发送消息
        /// </summary>
        /// <param name="uMsg"></param>
        /// <param name="wParam"></param>
        /// <param name="lParam"></param>
        /// <returns></returns>
        public bool PostMessage(int uMsg, IntPtr wParam = new IntPtr(), IntPtr lParam = new IntPtr())
        {
            return WinAPI.PostMessage(Hwnd, uMsg, wParam, lParam);
        }

        /// <summary>
        /// 阻塞发送消息
        /// </summary>
        /// <param name="uMsg"></param>
        /// <param name="wParam"></param>
        /// <param name="lParam"></param>
        /// <returns></returns>
        public IntPtr SendMessage(int uMsg, IntPtr wParam = new IntPtr(), IntPtr lParam = new IntPtr())
        {
            return WinAPI.SendMessage(Hwnd, uMsg, wParam, lParam);
        }

        /// <summary>
        /// 设置背景图片
        /// </summary>
        /// <param name="bitmap">图片数据</param>
        /// <param name="x">偏移X</param>
        /// <param name="y">偏移Y</param>
        /// <param name="dwRepeat">背景重复模式BIR_</param>
        /// <param name="rcGrids">九宫矩形</param>
        /// <param name="dwFlags">背景标识BIF_</param>
        /// <param name="dwAlpha">透明度</param>
        /// <param name="fUpdate">是否立即刷新</param>
        /// <returns></returns>
        public bool SetBackgroundImage(System.Drawing.Bitmap bitmap, int x, int y, int dwRepeat = BACKGROUND_REPEAT_ZOOM, ExRect rcGrids = default, int dwFlags = BACKGROUND_FLAG_DEFAULT, int dwAlpha = 255, bool fUpdate = true)
        {
            byte[] image = Util.BitmapToByte(bitmap);
            return ExAPI.Ex_ObjSetBackgroundImage(_handle.Handle, image, image == null ? 0 : image.Length, x, y, dwRepeat, ref rcGrids, dwFlags, dwAlpha, fUpdate);
        }

        /// <summary>
        /// 设置背景图片
        /// </summary>
        /// <param name="image">图片数据</param>
        /// <param name="x">偏移X</param>
        /// <param name="y">偏移Y</param>
        /// <param name="dwRepeat">背景重复模式BIR_</param>
        /// <param name="rcGrids">九宫矩形</param>
        /// <param name="dwFlags">背景标识BIF_</param>
        /// <param name="dwAlpha">透明度</param>
        /// <param name="fUpdate">是否立即刷新</param>
        /// <returns></returns>
        public bool SetBackgroundImage(byte[] image, int x, int y, int dwRepeat = BACKGROUND_REPEAT_ZOOM, ExRect rcGrids = default, int dwFlags = BACKGROUND_FLAG_DEFAULT, int dwAlpha = 255, bool fUpdate = true)
        {
            return ExAPI.Ex_ObjSetBackgroundImage(_handle.Handle, image, image.Length, x, y, dwRepeat, ref rcGrids, dwFlags, dwAlpha, fUpdate);
        }

        /// <summary>
        /// 设置背景图播放模式
        /// </summary>
        /// <param name="fPlayFrames">是否播放</param>
        /// <param name="fResetFrame">是否重置当前帧为首帧</param>
        /// <param name="fUpdate">是否立即刷新</param>
        /// <returns></returns>
        public bool SetBackgroundPlayState(bool fPlayFrames, bool fResetFrame, bool fUpdate)
        {
            return ExAPI.Ex_ObjSetBackgroundPlayState(_handle.Handle, fPlayFrames, fResetFrame, fUpdate);
        }

        /// <summary>
        /// 设置引擎数值
        /// </summary>
        /// <param name="nIndex">ENGINE_LONG_</param>
        /// <param name="nValue"></param>
        /// <returns></returns>
        public IntPtr SetLong(int nIndex, IntPtr nValue)
        {
            return ExAPI.Ex_DUISetLong(_handle.Handle, nIndex, nValue);
        }

        /// <summary>
        /// 移动窗口
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <param name="nWidth"></param>
        /// <param name="nHeight"></param>
        /// <param name="hEleAfter"></param>
        /// <param name="dwFlags"></param>
        /// <returns></returns>
        public bool SetPos(int x, int y, int nWidth, int nHeight, IntPtr hEleAfter, int dwFlags)
        {
            return WinAPI.SetWindowPos(Hwnd, hEleAfter, x, y, nWidth, nHeight, dwFlags);
        }

        /// <summary>
        /// 取组件自组件ID
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public ExControl GetObjFromID(int id)
        {
            ExControl ctrl = null;
            int hObj = ExAPI.Ex_ObjGetFromID(_handle.Handle, id);
            if (hObj != 0)
            {
                ctrl = new ExControl(new ExControlHandle(hObj));
            }
            return ctrl;
        }

        /// <summary>
        /// 取组件自组件名称
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public ExControl GetObjFromName(string name)
        {
            ExControl ctrl = null;
            int hObj = ExAPI.Ex_ObjGetFromName(_handle.Handle, name);
            if (hObj != 0)
            {
                ctrl = new ExControl(new ExControlHandle(hObj));
            }
            return ctrl;
        }

        /// <summary>
        /// 取组件自节点id
        /// </summary>
        /// <param name="nodeid"></param>
        /// <returns></returns>
        public ExControl GetObjFromNodeID(int nodeid)
        {
            ExControl ctrl = null;
            int hObj = ExAPI.Ex_ObjGetFromNodeID(_handle.Handle, nodeid);
            if (hObj != 0)
            {
                ctrl = new ExControl(new ExControlHandle(hObj));
            }
            return ctrl;
        }

        /// <summary>
        /// 取焦点组件
        /// </summary>
        /// <returns></returns>
        public ExControl GetFocus()
        {
            ExControl ctrl = null;
            int hObj = ExAPI.Ex_ObjGetFocus(_handle.Handle);
            if (hObj != 0)
            {
                ctrl = new ExControl(new ExControlHandle(hObj));
            }
            return ctrl;
        }

        /// <summary>
        /// 弹出菜单
        /// </summary>
        /// <param name="hMenu">菜单句柄</param>
        /// <param name="uFlags">相关常量 TPM_</param>
        /// <param name="x">弹出坐标X(屏幕坐标)</param>
        /// <param name="y">弹出坐标Y(屏幕坐标)</param>
        /// <param name="nReserved">保留</param>
        /// <param name="pfnWndProc">回调</param>
        /// <param name="dwFlags">相关常量 EMNF_</param>
        /// <returns></returns>
        public bool TrackPopupMenu(IntPtr hMenu, int uFlags, int x, int y, IntPtr nReserved, ExWndProcDelegate pfnWndProc, int dwFlags)
        {
            return ExAPI.Ex_TrackPopupMenu(hMenu, uFlags, x, y, nReserved, _handle.Handle, IntPtr.Zero, pfnWndProc, dwFlags);
        }

        /// <summary>
        /// 获取鼠标所在窗口组件句柄
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <param name="hParent"></param>
        /// <returns></returns>
        public ExControl GetObjFromPoint(int x, int y, int hParent)
        {
            ExControl ctrl = null;
            int hObj = ExAPI.Ex_DUIGetObjFromPoint(hParent, x, y);
            if (hObj != 0)
            {
                ctrl = new ExControl(new ExControlHandle(hObj));
            }
            return ctrl;
        }

        /// <summary>
        /// 寻找组件
        /// </summary>
        /// <param name="pObjChildAfter"></param>
        /// <param name="sClassName"></param>
        /// <param name="sTitle"></param>
        /// <returns></returns>
        public ExControl Find(ExControl pObjChildAfter = null, string sClassName = null, string sTitle = null)
        {
            int hObjAfter = pObjChildAfter == null ? 0 : pObjChildAfter.Handle;
            ExControl ctrl = null;
            int hObj = ExAPI.Ex_ObjFind(_handle.Handle, hObjAfter, sClassName, sTitle);
            if (hObj != 0)
            {
                ctrl = new ExControl(new ExControlHandle(hObj));
            }
            return ctrl;
        }

        /// <summary>
        /// 设置组件布局
        /// </summary>
        /// <param name="layout"></param>
        /// <returns></returns>
        public bool SetLayout(IBase layout)
        {
            return ExAPI.Ex_ObjLayoutSet(_handle.Handle, layout.Handle, true);
        }

        /// <summary>
        /// 设置托盘图标
        /// </summary>
        /// <param name="bitmap">图标</param>
        /// <param name="tipsStr">提示内容</param>
        /// <returns></returns>
        public bool SetTrayIcon(System.Drawing.Icon icon, string tipsStr)
        {
            return ExAPI.Ex_DUITrayIconSet(_handle.Handle, icon.Handle, tipsStr);
        }

        /// <summary>
        /// 设置托盘图标
        /// </summary>
        /// <param name="data">图标数据,可以是png,jpg</param>
        /// <param name="tipsStr">提示内容</param>
        /// <returns></returns>
        public bool SetTrayIcon(byte[] data, string tipsStr)
        {
            var icon = ExAPI.Ex_LoadImageFromMemory(data, (IntPtr)data.Length, IMAGE_ICON, 0);
            return ExAPI.Ex_DUITrayIconSet(_handle.Handle, icon, tipsStr);
        }

        /// <summary>
        /// 弹出托盘通知
        /// </summary>
        /// <param name="info">内容</param>
        /// <param name="title">标题</param>
        /// <param name="flags">常量NIIF_</param>
        /// <returns></returns>
        public bool PopupTrayIcon(string info, string title, int flags)
        {
            return ExAPI.Ex_DUITrayIconPopup(_handle.Handle, info, title, flags);
        }
    }
}
